package com.tiancheng.trade.settlement.service.settlement.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tiancheng.trade.settlement.enums.ErrorTypeEnum;
import com.tiancheng.trade.settlement.enums.SettlementBillStatusEnum;
import com.tiancheng.trade.settlement.exception.BusinessException;
import com.tiancheng.trade.settlement.mapper.settlement.*;
import com.tiancheng.trade.settlement.model.settlement.SettlementBillResult;
import com.tiancheng.trade.settlement.model.settlement.SettlementOldSystem;
import com.tiancheng.trade.settlement.model.settlement.SettlementRule;
import com.tiancheng.trade.settlement.service.settlement.ISettlementBillService;
import com.tiancheng.trade.settlement.util.JacksonJsonUtil;
import com.tiancheng.trade.settlement.util.UserUtils;
import com.tiancheng.trade.settlement.vo.settlement.admin.SettlementBillHandleVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;


/**
 * 对账业务逻辑
 *
 * @author wangxin
 */
@Slf4j
@Service
public class SettlementBillServiceImpl implements ISettlementBillService {

    @Autowired
    private SettlementWaitBillMapper settlementWaitBillMapper;
    @Autowired
    private SettlementBankWaitBillMapper settlementBankWaitBillMapper;
    @Autowired
    private SettlementBillResultMapper settlementBillResultMapper;
    @Autowired
    private SettlementBankBillDetailMapper settlementBankBillDetailMapper;
    @Autowired
    private SettlementOldSystemMapper settlementOldSystemMapper;
    @Resource
    private JacksonJsonUtil jacksonJsonUtil;
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public List<SettlementBillResult> reconciliation(List<String> subOrderKeys) {
        try {
            List<SettlementBillResult> billResults = this.getSuccessResultListKey(subOrderKeys, null);
            List<String> errorSubOrderIds = new ArrayList<String>();
            if (null == billResults || billResults.isEmpty()) {
                errorSubOrderIds.addAll(subOrderKeys);
            } else {
                //设置对账状态为对账成功
                billResults.stream().forEach(billResult -> billResult
                        .setStatus(SettlementBillStatusEnum.SUCCESS.getCode()));
                List<String> successSubOrderKeys = new ArrayList<String>();
                setSubOrderIdAndCostType(successSubOrderKeys, billResults);
                //获取对账失败的子订单号
                setStringList(errorSubOrderIds, subOrderKeys, successSubOrderKeys);
            }
            if (!errorSubOrderIds.isEmpty()) {
                List<String> platformNoDatas = new ArrayList<>();
                //查询对账失败的数据，剩下的就是银行有平台无的
                List<SettlementBillResult> errorBillResults = this.getFailResultListKey(errorSubOrderIds, null);
                if (null == errorBillResults || errorBillResults.isEmpty()) {
                    platformNoDatas.addAll(errorSubOrderIds);
                } else {
                    errorBillResults.stream().forEach(billResult -> billResult
                            .setStatus(SettlementBillStatusEnum.ABNORMAL.getCode()));
                    billResults.addAll(errorBillResults);
                    List<String> errors = new ArrayList<>();
                    setSubOrderIdAndCostType(errors, errorBillResults);
                    setStringList(platformNoDatas, errorSubOrderIds, errors);
                }
                List<SettlementBillResult> successAndFails = null;
                if (!platformNoDatas.isEmpty()) {
                    //银行有我方无的数据进行回掏操作（目前只在对账中心数据库表中回掏）
                    successAndFails = platformNoDataHandle(platformNoDatas, billResults);
                }

                if (null != successAndFails && !successAndFails.isEmpty()) {
                    log.info("银行有我方无的数据进行回掏操作{}:" + successAndFails.size());
                    List<String> successAndFailKeys = new ArrayList<String>();
                    for (SettlementBillResult re : successAndFails) {
                        successAndFailKeys.add(re.getSubOrderId() + re.getCostType() + re.getTradeType());
                        //更新对账成功或失败的数据
                        settlementBillResultMapper.updateBankDataToResult(re);
                    }
                    //更新银行待对账数据状态
                    settlementBankWaitBillMapper.updateStatus(successAndFailKeys);
                    settlementBankBillDetailMapper.updateStatus(successAndFailKeys);
                }
            }

            //平台有
          /*  billResults.stream().filter(billResult -> StringUtils.isEmpty(billResult.getSystemDataFlag())).forEach(orderFinish -> orderFinish
					.setSystemDataFlag(Integer.parseInt(ErrorTypeEnum.PLATFORM_YES.getCode())));*/
            billResults.stream().forEach(orderFinish -> {
                if (StringUtils.isEmpty(orderFinish.getSystemDataFlag())) {
                    orderFinish.setSystemDataFlag(Integer.parseInt(ErrorTypeEnum.PLATFORM_YES.getCode()));
                }
                orderFinish.setCompleteDate(orderFinish.getBankClearDate());
            });
            return billResults;
        } catch (Exception e) {
            log.info("清分对账出现异常:" + e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    private void setStringList(List<String> rtn, List<String> param, List<String> condition) {
        for (String sub : param) {
            if (!condition.contains(sub)) {
                rtn.add(sub);
            }
        }
    }

    private void setSubOrderIdAndCostType(List<String> subOrderKey, List<SettlementBillResult> results) {
        for (SettlementBillResult res : results) {
            subOrderKey.add(res.getSubOrderId() + res.getCostType() + res.getTradeType());
        }
    }

    /**
     * 获取所有银行侧有我方无的数据，去对账结果表里查询，看是否存在，如果存在，且
     * 未手工备注过，则进行对账，更新对账结果，如果手工备注过，则直接修改备注。
     * 如果对账结果表无数据则去账单表查询，如果有，则说明此账单异常，未重新匹配规则，
     * 则此账单保存对账结果为对账失败即可。如果没有，则是Bug，需要去订单中心回讨数据进行对账（目前没有做这一步操作）。
     */
    private List<SettlementBillResult> platformNoDataHandle(List<String> errorBillResults, List<SettlementBillResult> billResults) {
        List<SettlementBillResult> successAndFails = new ArrayList<>();
        //errorBillResults中都是银行有而我方没有的账单
        //银行有我方无需要处理的场景1：对接结果表匹配对账金额正确与错误的数据，2：已手工备注，3：未匹配清分规则，未对账，4：系统中没有对账数据
        List<String> subOrderKeys = getBankAndPlatformReconciliations(successAndFails, errorBillResults);
        if (subOrderKeys.isEmpty()) {
            return successAndFails;
        }
        //subOrderKeys为需要用来查询对账失败账单的子订单Key.
        //查询对账异常数据，返回的数据为查询手工备注的
        List<String> manualNotes = getBankAndPlatformExceptionReconciliations(successAndFails, subOrderKeys);
        if (manualNotes.isEmpty()) {
            return successAndFails;
        }
        //查询手工备注的数据，返回的数据为未配置清分规则的
        List<String> billDatas = getManualNotesData(successAndFails, manualNotes);
        if (billDatas.isEmpty()) {
            return successAndFails;
        }
        //查询未配置清分规则的数据，返回的为银行有，平台无的数据，一律保存结果为对账异常。
        List<String> endDatas = getRuleIsNullBillData(billResults, billDatas);
        if (endDatas.isEmpty()) {
            return successAndFails;
        }
        List<SettlementBillResult> results = addResults(endDatas);
        if (results != null && !results.isEmpty()) {
            //平台无的数据
            results.stream().forEach(billResult -> {
                billResult.setStatus(SettlementBillStatusEnum.PLATFORM_NOT_MATCH.getCode());
                billResult.setIsException(true);
                billResult.setSystemDataFlag(Integer.parseInt(ErrorTypeEnum.PLATFORM_NO.getCode()));
            });
            billResults.addAll(results);
        }
        return successAndFails;
    }

    private List<SettlementBillResult> addResults(List<String> endDatas) {
        return settlementBillResultMapper.selectPollingNoDataBankColumn(endDatas);
    }

    private List<String> getRuleIsNullBillData(List<SettlementBillResult> billResults, List<String> billDatas) {
        //查询未匹配清分规则，未对账的数据
        List<SettlementBillResult> ruleNullDatas = settlementBillResultMapper.queryRuleIsNullBillData(billDatas);
        if (ruleNullDatas == null || ruleNullDatas.isEmpty()) {
            return billDatas;
        }
        List<String> ruleExceptions = new ArrayList<String>();
        for (SettlementBillResult result : ruleNullDatas) {
            result.setIsException(true);
            if (StringUtils.isEmpty(result.getMerchantCode())) {
                result.setStatus(SettlementBillStatusEnum.PLATFORM_NOT_MATCH.getCode());
                result.setRemark(SettlementBillStatusEnum.PLATFORM_NOT_MATCH.getDesc());
            } else {
                result.setStatus(SettlementBillStatusEnum.ABNORMAL.getCode());
                result.setRemark(SettlementBillStatusEnum.ABNORMAL.getDesc());
            }
            ruleExceptions.add(result.getSubOrderId() + result.getTradeType());
        }
        //更新清分规则异常的这部分数据的状态，不再在待对账表显示
        settlementWaitBillMapper.updateRuleExceptionStatus(ruleExceptions);
        List<String> errorDatas = new ArrayList<>();
        setSubOrderIdAndCostType(errorDatas, ruleNullDatas);
        List<String> rtnList = new ArrayList<>();
        setStringList(rtnList, billDatas, errorDatas);
        billResults.addAll(ruleNullDatas);
        return rtnList;

    }

    private List<String> getBankAndPlatformReconciliations(List<SettlementBillResult> successAndFails, List<String> errorSubOrderIds) {
        //拿所有银行有我方没有的账单去对账结果中对账，能对上的则直接更新为对账成功。
        List<SettlementBillResult> successLists = settlementBillResultMapper.bankAndPlatformReconciliations(errorSubOrderIds);
        List<String> subOrderKeys = new ArrayList<>();
        if (null != successLists && !successLists.isEmpty()) {
            successLists.stream().forEach(billResult -> billResult
                    .setStatus(SettlementBillStatusEnum.SUCCESS.getCode()));
            successAndFails.addAll(successLists);
            List<String> errorSubOrderKeys = new ArrayList<>();
            setSubOrderIdAndCostType(errorSubOrderKeys, successLists);
            setStringList(subOrderKeys, errorSubOrderIds, errorSubOrderKeys);
        }
        //如果没有对账成功的数据，则所有银行有我方无的数据去结果表匹配金额异常的情况
        if (subOrderKeys.isEmpty()) {
            subOrderKeys.addAll(errorSubOrderIds);
        }
        return subOrderKeys;
    }

    private List<String> getBankAndPlatformExceptionReconciliations(List<SettlementBillResult> successAndFails
            , List<String> subOrderKeys) {
        List<SettlementBillResult> errorLists = settlementBillResultMapper.bankAndPlatformExceptionReconciliations(subOrderKeys);
        //过滤了对账成功和失败的数据，剩下的数据查询是否有手工备注
        List<String> manualNotes = new ArrayList<>();
        if (null != errorLists && !errorLists.isEmpty()) {
            errorLists.stream().forEach(errorBil -> {
                        errorBil.setStatus(SettlementBillStatusEnum.ABNORMAL.getCode());
                        errorBil.setIsException(true);
                    }
            );
            successAndFails.addAll(errorLists);
            List<String> errorSubOrderKeys = new ArrayList<>();
            setSubOrderIdAndCostType(errorSubOrderKeys, errorLists);
            setStringList(manualNotes, subOrderKeys, errorSubOrderKeys);
        } else {
            manualNotes.addAll(subOrderKeys);
        }
        return manualNotes;
    }

    private List<String> getManualNotesData(List<SettlementBillResult> successAndFails, List<String> manualNotes) {
        List<SettlementBillResult> manualNoteLists = null;
        //有可能到这里已经没有失败的数据了
        if (!manualNotes.isEmpty()) {
            //查询手工备注过的数据
            manualNoteLists = settlementBillResultMapper.queryManualNotesData(manualNotes);
        }
        if (null == manualNoteLists || manualNoteLists.isEmpty()) {
            return manualNotes;
        }
        manualNoteLists.stream().forEach(billResult -> billResult
                .setErrorRemark(billResult.getErrorRemark() + "，原账单已手工备注"));
        successAndFails.addAll(manualNoteLists);
        List<String> billDatas = new ArrayList<>();
        setSubOrderIdAndCostType(billDatas, manualNoteLists);
        List<String> rtnList = new ArrayList<>();
        setStringList(rtnList, manualNotes, billDatas);
        return rtnList;
    }

    private List<SettlementBillResult> getSuccessResultListKey(List<String> subOrderKeys, String rematch) {
        List<SettlementBillResult> billResults = settlementBillResultMapper.getReconciliationResult(subOrderKeys, rematch);
        if (null == billResults) {
            return null;
        }
        //设置对账状态为对账成功
        billResults.stream().forEach(billResult -> billResult
                .setStatus(SettlementBillStatusEnum.SUCCESS.getCode()));
        return billResults;

    }

    private List<SettlementBillResult> getFailResultListKey(List<String> errorSubOrderIds, String rematch) {
        List<SettlementBillResult> errorBillResults = settlementBillResultMapper.getExceptionReconciliationResult(errorSubOrderIds, rematch);
        if (errorBillResults == null) {
            errorBillResults = new ArrayList<>();
        }
        //对账失败
        errorBillResults.stream().filter(errorBil -> !StringUtils.isEmpty(errorBil.getMerchantCode())).forEach(errorBil -> {
            errorBil.setStatus(SettlementBillStatusEnum.ABNORMAL.getCode());
            errorBil.setErrorAmount((errorBil.getClearAmount() - errorBil.getBankClearAmount()));
        });
        return errorBillResults;

    }

    /* @Transactional
     @Override
     public void saveResult(List<SettlementBillResult> list, List<String> subOrderKey) {
         int count = 0;

         for(SettlementBillResult bankWaitBill:list) {
             try {
             log.info("单条循环{}",bankWaitBill.getSubOrderId() + bankWaitBill.getCostType()+bankWaitBill.getTradeType());
             count = settlementBillResultMapper.insert(bankWaitBill);
             log.info("是否成功{}",count);
             if(count>0) {
                   List<String> test = new ArrayList<String>();
                   test.add(bankWaitBill.getSubOrderId() + bankWaitBill.getCostType()+bankWaitBill.getTradeType());
                   settlementWaitBillMapper.updateStatus(test);
                   settlementBankWaitBillMapper.updateStatus(test);
                   settlementBankBillDetailMapper.updateStatus(test);
             }
             } catch (Exception e) {
                 log.error("saveResult保存对账数据时出现异常{}",e.getMessage());
                 log.error("异常的子订单{}",bankWaitBill.getSubOrderId() + bankWaitBill.getCostType()+bankWaitBill.getTradeType());
                 e.printStackTrace();
             }
         }


     }*/
    @Transactional
    @Override
    public void saveResult(List<SettlementBillResult> list, List<String> subOrderKey) {
        int count = 0;
        try {
            log.info("saveResult执行{}", list.size());
            count = settlementBillResultMapper.batchSave(list);
            log.info("保存清分对账数据成功...{}", count);
            if (count > 0) {
                log.info("更新待对账表状态{}");
                settlementWaitBillMapper.updateStatus(subOrderKey);
                settlementBankWaitBillMapper.updateStatus(subOrderKey);
                settlementBankBillDetailMapper.updateStatus(subOrderKey);
            }
        } catch (Exception e) {
            log.error("saveResult保存对账数据时出现异常{}", e.getMessage());
            e.printStackTrace();
        }
        log.info("保存成功的总数{}", count);
    }


    @Override
    public int audit(SettlementBillHandleVO vo) throws BusinessException {
        if (vo.getCodes() == null || vo.getCodes().isEmpty()) {
            return 0;
        }
        vo.setUser(UserUtils.currentUserName());
        try {
            int count = settlementBillResultMapper.updateOldSystemDataStatus(vo);
            if (count > 0 && ErrorTypeEnum.PLATFORM_NO.getCode().equals(vo.getErrorType())) {
                QueryWrapper<SettlementBillResult> conditions = new QueryWrapper<>();
                conditions.in("bank_order_id", vo.getCodes());
                List<SettlementBillResult> subOrders = settlementBillResultMapper.selectList(conditions);
                if (subOrders != null && !subOrders.isEmpty()) {
                    List<SettlementOldSystem> oldSystems = objectMapper.readValue(jacksonJsonUtil.toJson(subOrders), new TypeReference<List<SettlementOldSystem>>() {
                    });
                    return settlementOldSystemMapper.batchSave(oldSystems);
                }
            }
            return count;
        } catch (Exception e) {
            log.info("audit方法出现异常：{}", e.getMessage());

        }
        return 0;
    }

    @Transactional
    @Override
    public void saveBankNothingException() {
        log.info("清分对账定时任务 银行侧数据为空，查询中心侧是否有待对账数据，有则记录异常");
        List<SettlementBillResult> results = settlementBillResultMapper.queryBankNothingList();
        if (results == null || results.isEmpty()) {
            log.info("saveBankNothingException return null");
            return;
        }
        log.info("我方有平台无的数据{}", results.size());
        // 异常订单sub_order_id
        List<String> subOrderKeys = new ArrayList<String>();
        setSubOrderIdAndCostType(subOrderKeys, results);
        results.stream().
                forEach(errorBil -> {
                            errorBil.setStatus(SettlementBillStatusEnum.BANK_NOT_MATCH.getCode());
                            errorBil.setSystemDataFlag(Integer.parseInt(ErrorTypeEnum.PLATFORM_YES.getCode()));
                            errorBil.setRemark(SettlementBillStatusEnum.BANK_NOT_MATCH.getDesc());
                            Integer clearAmount = errorBil.getClearAmount() == null ? 0 : errorBil.getClearAmount();
                            errorBil.setErrorAmount(clearAmount);
                            errorBil.setCompleteDate(LocalDateTime.now());
                        }
                );
        //批量保存异常订单、更新待对账表状态
        this.saveResult(results, subOrderKeys);
    }

    @Async
    @Override
    public void matchingRuleReconciliation(List<String> subOrderKeys, SettlementRule rule)
            throws BusinessException {
        List<SettlementBillResult> billResults = this.getSuccessResultListKey(subOrderKeys, "true");
        List<String> successSubOrderKeys = new ArrayList<String>();
        List<String> errorSubOrderIds = new ArrayList<String>();
        if (null == billResults || billResults.isEmpty()) {
            errorSubOrderIds.addAll(subOrderKeys);
        } else {
            setSubOrderIdAndCostType(successSubOrderKeys, billResults);
            //获取对账失败的子订单号
            setStringList(errorSubOrderIds, subOrderKeys, successSubOrderKeys);
        }
        billResults.stream().forEach(successBil -> {
            successBil.setIsException(false);
            successBil.setStatus(SettlementBillStatusEnum.SUCCESS.getCode());
        });
        if (!errorSubOrderIds.isEmpty()) {
            List<SettlementBillResult> errorBillResults = this.getFailResultListKey(errorSubOrderIds, "true");
            errorBillResults.stream().forEach(errorBil -> {
                errorBil.setIsException(true);
                errorBil.setStatus(SettlementBillStatusEnum.ABNORMAL.getCode());
            });
            billResults.addAll(errorBillResults);
        }
        for (SettlementBillResult result : billResults) {
            //每条账单的清分金额不同只能循环单条修改
            result.setClearRuleCode(rule.getRuleCode());
            settlementBillResultMapper.updateBillResult(result);
        }
        settlementWaitBillMapper.updateStatus(subOrderKeys);
    }

    @Override
    public int polling() throws BusinessException {
        return settlementBillResultMapper.updateBankNothing();
    }

    @Override
    public void saveBankSubOrderIsNullData() {
        int count = settlementBillResultMapper.selectBankSubOrderIdIsNullData();
        if (count > 0) {
            settlementBankBillDetailMapper.updateSubOrderIdIsNullStatus();
            settlementBankWaitBillMapper.updateSubOrderIdIsNullStatus();
        }

    }

    @Override
    public void updateErrorAmount(List<String> subOrderKeys) {
        settlementBillResultMapper.updateErrorAmount(subOrderKeys);
    }


}
